#include "timer.h"

#include "evt_loop.h"

namespace qio {

class TimerCallbacks {
 public:
  static void OnTimerCallback(uv_timer_t *h) QIO_NOEXCEPT {
    auto *timer = static_cast<Timer *>(h->data);
    assert(timer != nullptr);
    if (timer->timeout_cb_) {
      timer->timeout_cb_(timer);
    }
  }

  static void OnTimerCloseCallback(uv_handle_t *h) QIO_NOEXCEPT {
    auto *timer = static_cast<Timer *>(h->data);
    assert(timer != nullptr);
    auto shared_ins = timer->ShareInstance();
    shared_ins->stat_ = Timer::kStopped;

    if (shared_ins->stop_cb_) {
      shared_ins->stop_cb_(timer);
    }

    shared_ins->timeout_cb_ = nullptr;
    shared_ins->stop_cb_ = nullptr;
  }
};

Timer::~Timer() QIO_NOEXCEPT { delete timer_; }

Timer::Timer(EventLoop *loop) QIO_NOEXCEPT : loop_(loop),
                                             timer_(new uv_timer_t),
                                             timeout_cb_(nullptr),
                                             stop_cb_(nullptr),
                                             stat_(kInit) {
  timer_->data = this;
}

void Timer::Start(std::size_t timeout_ms) QIO_NOEXCEPT {
  auto self = ShareInstance();
  auto start_details = [self, timeout_ms]() {
    // timer must `not` in stop progress. timer is not reusable.
    assert(self->stat_ <= kStopping);  // NOLINT
    if (self->stat_ == kInit) {
      uv_timer_init(self->loop_->GetRawLoop(), self->timer_);
    }
    uv_timer_start(self->timer_, &TimerCallbacks::OnTimerCallback, timeout_ms,
                   0);
    self->stat_ = kRunning;
  };
  if (loop_->IsInLoop()) {
    start_details();
  } else {
    loop_->RunInLoop(start_details);
  }
}

void Timer::Pause() QIO_NOEXCEPT {
  auto self = ShareInstance();
  auto pause_details = [self]() {
    if (self->stat_ >= kStopping) {
      return;
    }
    uv_timer_stop(self->timer_);
    self->stat_ = kPaused;
  };
  if (loop_->IsInLoop()) {
    pause_details();
  } else {
    loop_->RunInLoop(pause_details);
  }
}

void Timer::Stop() QIO_NOEXCEPT {
  auto self = ShareInstance();
  auto stop_details = [self]() {
    if (self->stat_ >= kStopping) {
      return;
    }
    self->Pause();
    self->stat_ = kStopping;
    uv_close(reinterpret_cast<uv_handle_t *>(self->timer_),
             &TimerCallbacks::OnTimerCloseCallback);
  };
  if (loop_->IsInLoop()) {
    stop_details();
  } else {
    loop_->RunInLoop(stop_details);
  }
}

}  // namespace qio